using Microsoft.VisualStudio.TestTools.UnitTesting;
using System.IO;
using System.Text;
using System;
using System.Collections.Generic;
using c_parser;
using System.Diagnostics.CodeAnalysis;
using libC.semantic;
using libC;
using libC.translate;

namespace testWrap
{
    [TestClass]
    public class UnitTest1
    {
        /// <summary>
        /// 寻找节点
        /// </summary>
        /// <param name="rootNode"></param>
        /// <param name="type"></param>
        /// <param name="text"></param>
        /// <returns></returns>
        private semanticASTNode findNode(semanticASTNode rootNode, string type, string text = null)
        {
            if (rootNode == null) return null;
            if (rootNode.type == type && (text == null || rootNode.text == text)) return rootNode;

            for (int i = 0, len = rootNode.children.Count; i < len; i++)
            {
                var c = rootNode.children[i];
                var _n = findNode(c, type, text);
                if (_n != null) return _n;
            }
            return null;
        }

        /// <summary>
        /// 寻找全部的节点
        /// </summary>
        /// <param name="rootNode"></param>
        /// <param name="outNodes"></param>
        /// <param name="type"></param>
        /// <param name="text"></param>
        private void findNodes(semanticASTNode rootNode, List<semanticASTNode> outNodes, string type, string text = null)
        {
            if (rootNode == null || outNodes == null) return;
            if (rootNode.type == type && (text == null || rootNode.text == text))
            {
                outNodes.Add(rootNode);
            }
            for (int i = 0, len = rootNode.children.Count; i < len; i++)
            {
                var c = rootNode.children[i];
                findNodes(c, outNodes, type, text);
            }
        }


        [TestMethod]
        public void voidTest()
        {
            Assert.AreEqual(1, 1, "应该为1");
        }

        //测试 Wrap 产生式匹配的元素
        [TestMethod]
        public void TestWrapProductionElement()
        {
            //var parser = ttlang.dumptool.GetParserInfo<WrapLexer, WrapParser>("prog");
            var parser = libC.tools.GetParserInfo<WrapLexer, WrapParser>("prog");
            //val
            string expr = File.ReadAllText($"./../../../../res/cLangTexts/testWrapCont.h");
            //Console.WriteLine($"== 源文件 == \n {expr}");
            //double rightVal;
            var ast = parser.Parse(expr, true, true, true);
            var vis = new WrapVisWords();
            //统计 识别 词
            var result = vis.Visit(ast);
            //
            var data = new Dictionary<string, int>()
            {
                ["DEFINE"] = 6,
                ["fun_decl"] = 8,
                ["decl"] = 15,
                ["struct_union"] = 4,
                ["c_enum"] = 6,
            };

            foreach (var item in data)
            {
                var k = item.Key;
                var v = item.Value;
                Assert.AreEqual(vis.dic[k], v, $"{k} 数量应该为 : {v}");
            }
        }

        //测试语义 , 枚举
        [TestMethod]
        public void TestSemanticEnum()
        {
            string filePath = $"./../../../../res/cLangTexts/testWrapEnum.h";
            var semAnaly = tools.getSemAnaly(filePath);
            semAnaly.parse();
            //
            var semantAst = semAnaly.root;
            var node = semAnaly.root.node;

            SemanticInfoType aType = SemanticInfoType.ENUM;
            var aNode = findNode(node, "c_enum", "enume11{one=XXX}");
            var info = (enumInfo)semantAst.getSemantInfo(aNode.id);
            //类型
            Assert.AreEqual(info.getType(), aType, $" 类型 应该为 : {aType}");
            //定义体内容
            Assert.AreEqual(info.enums_decl[0], "one", $"应该为 : {"one"}");
            Assert.AreEqual(info.enums_decl[1], "XXX", $"应该为 : {"XXX"}");

            var bNode = findNode(node, "c_enum", "enume2{ea,eb,ec}");
            var info_1 = (enumInfo)semantAst.getSemantInfo(bNode.id);
            //定义不赋值
            Assert.AreEqual(info_1.enums_decl[0], "ea", $"应该为 : {"ea"}");
            Assert.AreEqual(info_1.enums_decl[2], "eb", $"应该为 : {"eb"}");
        }


        //测试语义 , 结构体
        [TestMethod]
        public void TestSemanticStruct()
        {

            string filePaht = $"./../../../../res/cLangTexts/testWrapStruct.h";
            var semAnaly = tools.getSemAnaly(filePaht);
            semAnaly.parse();
            //tree node
            var semantAst = semAnaly.root;
            var node = semAnaly.root.node;
            var structNodes = new List<semanticASTNode>();
            findNodes(node, structNodes, "struct_union");
            //
            var stc1Node = structNodes[1];
            var stc1Info = (structInfo)semantAst.getSemantInfo(stc1Node.id);
            //类型 
            Assert.AreEqual(stc1Info.getType(), SemanticInfoType.STRUCT, $" 类型 应该为 : {SemanticInfoType.STRUCT}");
            //结构名
            Assert.AreEqual(stc1Info.name, "stc1", $" 名字 应该为 : {"stc1"}");
            //结构 定义
            //Assert.AreEqual(stc1Info.decls[0], "int", $" 应该为 : {"int"}");
            //Assert.AreEqual(stc1Info.decls[1], "a", $" 应该为 : {"a"}");

            //结构子定义
        }

        //测试语义 , 函数
        [TestMethod]
        public void TestSemanticFunction()
        {
            string filePaht = $"./../../../../res/cLangTexts/testWrapFunction.h";
            var semAnaly = tools.getSemAnaly(filePaht);
            semAnaly.parse();
            //tree node
            var semantAst = semAnaly.root;
            var node = semAnaly.root.node;
            var structNodes = new List<semanticASTNode>();
            findNodes(node, structNodes, "fun_decl");
            var fun0Info = (functionInfo)semantAst.getSemantInfo(structNodes[0].id);
            var fun21Info = (functionInfo)semantAst.getSemantInfo(structNodes[2].id);
            var fun3Info = (functionInfo)semantAst.getSemantInfo(structNodes[3].id);
            var fun4Info = (functionInfo)semantAst.getSemantInfo(structNodes[4].id);

            //类型
            Assert.AreEqual(fun0Info.getType(), SemanticInfoType.FUNCTION, $" 类型 应该为 : {SemanticInfoType.FUNCTION}");
            //函数名
            Assert.AreEqual(fun0Info.name, "fun0", $" 名字 应该为 : {"fun0"}");

            //参数
            //基本参数
            Assert.AreEqual(fun0Info.args[0], "int", $"  应该为 : {"int"}");
            Assert.AreEqual(fun0Info.args[1], "a", $"  应该为 : {"a"}");
            //无名参数
            Assert.AreEqual(fun3Info.args[0], "float", $"  应该为 : {"float"}");
            Assert.AreEqual(fun3Info.args[1], "p0", $"  应该为 : {"p0"}");
            //可变长参数
            Assert.AreEqual(fun3Info.args[4], null, $"  应该为 : {"null"}");
            Assert.AreEqual(fun3Info.args[5], "...", $"  应该为 : {"..."}");
            //数组参数
            Assert.AreEqual(fun4Info.args[0], "int[56]", $"  应该为 : {"int[56]"}");
            Assert.AreEqual(fun4Info.args[1], "t", $"  应该为 : {"t"}");
            //类型指针 参数
            Assert.AreEqual(fun4Info.args[2], "char*", $"  应该为 : {"char*"}");
            Assert.AreEqual(fun4Info.args[3], "c", $"  应该为 : {"c"}");
            //类型数组指针 参数
            Assert.AreEqual(fun4Info.args[4], "float*[31]", $"  应该为 : {"float*[31]"}");
            Assert.AreEqual(fun4Info.args[5], "a", $"  应该为 : {"a"}");
            //struct, 参数
            Assert.AreEqual(fun21Info.args[0], "structName", $"  应该为 : {"structName"}");
            Assert.AreEqual(fun21Info.args[1], "pa", $"  应该为 : {"pa"}");
            //union, 参数
            Assert.AreEqual(fun21Info.args[2], "tUnion", $"  应该为 : {"tUnion"}");
            Assert.AreEqual(fun21Info.args[3], "u", $"  应该为 : {"u"}");
            //enum , 参数
            Assert.AreEqual(fun21Info.args[4], "week*", $"  应该为 : {"week*"}");
            Assert.AreEqual(fun21Info.args[5], "ww", $"  应该为 : {"ww"}");

            //头定义限定词
            Assert.AreEqual(fun0Info.firstID, "static", $"  应该为 : {"static"}");
            Assert.AreEqual(fun21Info.firstID, "EXPORT_API", $"  应该为 : {"EXPORT_API"}");
            Assert.AreEqual(fun4Info.firstID, "EXPORT_API", $"  应该为 : {"EXPORT_API"}");
        }

        //测试 C 语言 wrap Code 生成
        [TestMethod]
        public void TestCWrapCodeGen()
        {
            string projName = "testCProject";
            string projPath = $"./../../../../res/cLangTexts/{projName}";
            //string filePaht = $"./../../../../res/cLangTexts/testWrapFunction.h";
            //var semAnaly = getSemAnaly(filePaht);
            var semAnaly = tools.getSemAnalyByProj(projPath);
            semAnaly.parse();
            string savePath = $"./../../../../TestGenWrapCodes/{projName}/";
            if (!Directory.Exists(savePath)) {
                Directory.CreateDirectory(savePath);
            }
            //C wrap code gen
            string c_codeStr = wrapCodeGenerator.codeGen(semAnaly, wrapLang.C , "xx.dll","","TTEngine");
            Console.WriteLine($"生成的wrap代码 C ：\n {c_codeStr}");

            //保存 wrap文件
            string c_wrapFileName = "Wrap";
            var codes = c_codeStr.Split(tools.CODESPLITE);
            File.WriteAllText($"{savePath}{c_wrapFileName}.h", codes[0]);
            File.WriteAllText($"{savePath}{c_wrapFileName}.cpp", codes[1]);

            //C# wrap code gen
            //string cs_codeStr = wrapCodeGenerator.codeGen(semAnaly, wrapLang.C_SHARP, "xx.dll", "", "");    
            string cs_codeStr = wrapCodeGenerator.codeGen(semAnaly, wrapLang.C_SHARP, "xx.dll", "", "", false);   //强制安全模式
            Console.WriteLine($"生成的wrap代码 C# ：\n {cs_codeStr}");

            //保存 wrap文件
            string cs_wrapFileName = "Wrap.cs";
            File.WriteAllText($"{savePath}{cs_wrapFileName}", cs_codeStr);

            //断言
            Assert.AreEqual(true, c_codeStr.Length > 0, $" c code 应该为 ");
            Assert.AreEqual(true, cs_codeStr.Length > 0, $" C# code  应该为 ");
        }

    }

    //查看 wrap 识别的 词
    public class WrapVisWords : WrapBaseVisitor<string>
    {

        public Dictionary<string, int> dic = new Dictionary<string, int>();

        private void touch(string key)
        {
            if (!dic.ContainsKey(key))
            {
                dic[key] = 0;
            }
            dic[key]++;
        }
        public override string VisitProg( WrapParser.ProgContext context)
        {
            return base.VisitProg(context);
            //return base.Visit(context.GetChild(1));
        }

        public override string VisitRoot( WrapParser.RootContext context)
        {

            if (context.DEFINE() != null)
            {
                touch("DEFINE");
            }
            else if (context.fun_decl() != null)
            {

                touch("fun_decl");
            }
            else if (context.decl() != null)
            {
                touch("decl");
                return base.VisitRoot(context);

            }
            return "";
            //if (context.ChildCount < 1) return "";
            //return base.Visit(context.children[0]);
        }
        public override string VisitStruct_union( WrapParser.Struct_unionContext context)
        {
            touch("struct_union");
            //return base.VisitStruct_union(context);
            return "";
        }
        public override string VisitC_enum( WrapParser.C_enumContext context)
        {
            touch("c_enum");
            return base.VisitC_enum(context);
        }


    }
}